using System;

using Supremacy.Annotations;
using Supremacy.Game;
using Supremacy.Types;

namespace Supremacy.Personnel
{
    public abstract class AgentAssignment : IGameTurnListener
    {
        private bool _isAssigned;
        private GameObjectID _assignedAgentId;
        private GameObjectID _assignedAgentCivId;

        public abstract string StatusText { get; }
        public abstract string DescriptionText { get; }

        public bool IsAssigned
        {
            get { return _isAssigned; }
        }

        public virtual bool IsCompleted
        {
            get
            {
                var turnsRemaining = this.ProgressInternal;

                return turnsRemaining.HasValue &&
                       turnsRemaining.Value == 100f;
            }
        }

        public bool IsProgressKnown
        {
            get { return this.ProgressInternal.HasValue; }
        }

        public Percentage Progress
        {
            get
            {
                var progress = this.ProgressInternal;
                return progress.HasValue ? progress.Value : Percentage.MinValue;
            }
        }

        protected virtual Percentage? ProgressInternal
        {
            get { return null; }
        }

        public Agent AssignedAgent
        {
            get
            {
                if (!_assignedAgentId.IsValid || !_assignedAgentCivId.IsValid)
                    return null;

                Agent agent;
                CivilizationManager civManager;
                
                if (!GameContext.Current.CivilizationManagers.TryGetValue(_assignedAgentCivId, out civManager))
                    return null;

                if (!civManager.AgentPool.CurrentAgents.TryGetValue(_assignedAgentCivId, out agent))
                    return null;

                return agent;
            }
        }

        public bool CanAssign([NotNull] Agent agent)
        {
            if (agent == null)
                throw new ArgumentNullException("agent");

            if (this.IsAssigned)
                return false;

            if (agent.Owner == null)
                return false;

            CivilizationManager civManager;

            if (!GameContext.Current.CivilizationManagers.TryGetValue(agent.OwnerID, out civManager))
                return false;

            if (!civManager.AgentPool.CurrentAgents.Contains(agent))
                return false;

            return this.CanAssignCore(agent);
        }

        public void Assign([NotNull] Agent agent)
        {
            if (agent == null)
                throw new ArgumentNullException("agent");

            if (!this.CanAssign(agent))
            {
                throw new ArgumentException(
                    string.Format("Assignment cannot be give to agent '{0}'.", agent.Profile.Name),
                    "agent");
            }

            this.AssignCore(agent);

            agent.Assignment = this;

            _assignedAgentId = agent.ObjectID;
            _assignedAgentCivId = agent.OwnerID;
            _isAssigned = true;
        }

        public void Unassign()
        {
            if (!_isAssigned)
                return;

            var agent = this.AssignedAgent;

            try
            {
                this.UnassignCore();
                agent.Assignment = null;
            }
            finally
            {
                _isAssigned = false;
                _assignedAgentId = GameObjectID.InvalidID;
                _assignedAgentCivId = GameObjectID.InvalidID;
            }
        }

        protected abstract bool CanAssignCore([NotNull] Agent agent);
        protected abstract void AssignCore([NotNull] Agent agent);
        protected abstract void UnassignCore();

        protected virtual void OnTurnStarted() {}
        protected virtual void OnTurnPhaseStarted(TurnPhase phase) {}
        protected virtual void OnTurnPhaseFinished(TurnPhase phase) {}
        protected virtual void OnTurnFinished() {}

        #region Implementation of IGameTurnListener

        void IGameTurnListener.OnTurnStarted(GameContext game)
        {
            GameContext.PushThreadContext(game);
            try { OnTurnStarted(); }
            finally { GameContext.PopThreadContext(); }
        }

        void IGameTurnListener.OnTurnPhaseStarted(GameContext game, TurnPhase phase)
        {
            GameContext.PushThreadContext(game);
            try { OnTurnPhaseStarted(phase); }
            finally { GameContext.PopThreadContext(); }
        }

        void IGameTurnListener.OnTurnPhaseFinished(GameContext game, TurnPhase phase)
        {
            GameContext.PushThreadContext(game);
            try { OnTurnPhaseFinished(phase); }
            finally { GameContext.PopThreadContext(); }
        }

        void IGameTurnListener.OnTurnFinished(GameContext game)
        {
            GameContext.PushThreadContext(game);
            try { OnTurnFinished(); }
            finally { GameContext.PopThreadContext(); }
        }

        #endregion
    }
}